package com.lap.mybatis.starter.plugins;

import com.lap.context.starter.helper.UserHelper;
import com.lap.framework.entity.Audit;
import com.lap.mybatis.starter.annotation.MyBatch;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 *
 *
 * <pre>
 * Mybatis Plugin 自定义，实现对更新和插入操作的修改人和更新人字段赋值.
 * 参考 Mybatis 官网: {@code https://mybatis.org/mybatis-3/zh/configuration.html#plugins}
 *
 * {@code
 * <plugins>
 *   <plugin interceptor="com.example.mybatis.plugin.AuditFieldInterceptor">
 *     <property name="createdByField" value="createUser"/>
 *     <property name="updatedByField" value="updateUser"/>
 *   </plugin>
 * </plugins>
 * }
 * </pre>
 *
 * @author Shuisheng.Lao(劳水生)
 * @version 0.0.1
 * @see MyBatch
 */
@Slf4j
@Intercepts({
  @Signature(
      type = Executor.class,
      method = "update",
      args = {MappedStatement.class, Object.class})
})
public class AuditFieldInterceptor implements Interceptor {

  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    try {
      Object[] args = invocation.getArgs();
      if (ArrayUtils.isNotEmpty(args)) {
        Arrays.stream(args).forEach(this::processArgument);
      }
    } catch (Exception e) {
      log.warn("Error While setting createdBy/updatedBy: {}", e.getMessage(), e);
    }
    return invocation.proceed();
  }

  private void processArgument(Object arg) {
    if (arg instanceof List<?> list) {
      list.forEach(this::processArgument);
    } else if (arg instanceof Map<?, ?> map) {
      map.values().forEach(this::processArgument);
    } else if (arg instanceof Audit audit) {
      setEntityProperties(audit);
    }
  }

  private void setEntityProperties(Audit audit) {
    String username = UserHelper.getFullName();
    audit.setCreatedBy(StringUtils.defaultIfBlank(audit.getCreatedBy(), username));
    audit.setUpdatedBy(StringUtils.defaultIfBlank(audit.getUpdatedBy(), username));
  }

  @Override
  public Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }

  @Override
  public void setProperties(Properties properties) {
    log.info("Initializing plugin with properties: {}", properties);
  }
}
